/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.transas.zorlog.gcslink;

import java.io.IOException;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author amalikov
 */
public class UDPStream implements SerialStream {
	
	public UDPStream(String self, int selfPort, String remote, int remotePort) {
		super();
		this.self = self;
		this.selfPort = selfPort;
		this.remote = remote;
		this.remotePort = remotePort;
		buffer = new byte[1024];
		bufferPos = 0;
		dataInBuffer = 0;
		timeout = 200;
	}
	
	private void open() throws IOException {
		socketIn = new DatagramSocket(new InetSocketAddress(self, selfPort));
		socketIn.setReuseAddress(true);
		socketOut = new DatagramSocket(new InetSocketAddress(self, 0));
		socketOut.setReuseAddress(true);
		remoteAddr = InetAddress.getByName(remote);
	}
	
	@Override
	public void close() {
		if (socketIn != null) {
			socketIn.close();
			socketIn = null;
		}
		
		if (socketOut != null) {
			socketOut.close();
			socketOut = null;
		}
	}
	
	@Override
	public int read(byte[] data, int ofs, int len) {
		int result = 0;
		int size = len;

		if (dataInBuffer >= size) {
			// если данных в буфере больше чем надо
			for (int i = 0; i < size; ++i)
				data[ofs + i] = buffer[bufferPos + i];

			result = size;
			bufferPos += size;
			dataInBuffer -= size;
		} else {
			// если данных в буфере меньше чем надо
			int pos = 0;

			if (dataInBuffer > 0) {
				// прочитать остатки
				for (int i = 0; i < dataInBuffer; ++i)
					data[ofs + i] = buffer[bufferPos + i];

				result = dataInBuffer;
				pos = dataInBuffer;
				size -= dataInBuffer;
			}

			while (size > 0) {
				dataInBuffer = 0;
				bufferPos = 0;

				// принять новую датаграму
				DatagramPacket p = new DatagramPacket(buffer, buffer.length);
				try {
					open();
					socketIn.setSoTimeout(timeout);
					socketIn.receive(p);
				} catch (SocketTimeoutException ex) {
					return result;
				} catch (IOException ex) {
					Logger.getLogger(UDPStream.class.getName()).log(Level.SEVERE, null, ex);
				}
				
				dataInBuffer = p.getLength();
				
				int n = Math.min(dataInBuffer, size);	// читаем меньше или равно того что приняли
				
				for (int i = 0; i < n; ++i)
					data[ofs + pos + i] = buffer[i];

				pos += n;
				result += n;
				bufferPos = n;
				dataInBuffer -= n;
				size -= n;
			}
		}
	
		return result;
	}
	
	@Override
	public int read() {
		byte[] a = new byte[1];
		int res = read(a, 0, 1);
		return (res > 0) ? a[0] : -1;
	}
	
	@Override
	public void write(byte[] data, int ofs, int len) {
		try {
			open();
			socketOut.send(new DatagramPacket(data, ofs, len, remoteAddr, remotePort));
		} catch (IOException ex) {
			Logger.getLogger(UDPStream.class.getName()).log(Level.SEVERE, null, ex);
		}
	}
	
	@Override
	public void clean() {
		dataInBuffer = 0;
		bufferPos = 0;
	}
	
	private final String self;
	private final int selfPort;
	private final String remote;
	private final int remotePort;
	private InetAddress remoteAddr;
	
	private DatagramSocket socketIn;
	private DatagramSocket socketOut;
	
	private final byte[] buffer;
	private int bufferPos;
	private int dataInBuffer;
	private final int timeout;
}
